

package com.junjie.index12306.biz.ticketservice.canal;

import cn.hutool.core.collection.CollUtil;
import com.junjie.index12306.biz.ticketservice.common.enums.CanalExecuteStrategyMarkEnum;
import com.junjie.index12306.biz.ticketservice.remote.TicketOrderRemoteService;
import com.junjie.index12306.biz.ticketservice.remote.dto.TicketOrderDetailRespDTO;
import com.junjie.index12306.biz.ticketservice.remote.dto.TicketOrderPassengerDetailRespDTO;
import com.junjie.index12306.biz.ticketservice.service.handler.ticket.dto.TrainPurchaseTicketRespDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.junjie.index12306.biz.ticketservice.mq.event.CanalBinlogEvent;
import com.junjie.index12306.biz.ticketservice.service.SeatService;
import com.junjie.index12306.biz.ticketservice.service.handler.ticket.tokenbucket.TicketAvailabilityTokenBucket;
import com.junjie.index12306.framework.starter.common.toolkit.BeanUtil;
import com.junjie.index12306.framework.starter.convention.result.Result;
import com.junjie.index12306.framework.starter.designpattern.strategy.AbstractExecuteStrategy;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 订单关闭或取消 后置处理组件
 * 通过令牌限流容器可以有效防止座位超卖问题。用户把令牌申请完了就开始返回快速失败了，余票可能会有一个小的延迟更新。
 * 这个延迟更新余票时间，大概会有300-1000ms，不管咋说，肯定比官方 12306 要快。
 * 12306 的这种余票更新策略肯定有他们的原因，不过目前我还没有想到解决方案，大家有想法的可以大胆猜测，或者去网上找一些论文验证。
 * 
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderCloseCacheAndTokenUpdateHandler implements AbstractExecuteStrategy<CanalBinlogEvent, Void> {

    private final TicketOrderRemoteService ticketOrderRemoteService;
    private final SeatService seatService;
    private final TicketAvailabilityTokenBucket ticketAvailabilityTokenBucket;

    @Override
    public void execute(CanalBinlogEvent message) {
        // 1、从新数据中选出状态不为空，且状态为 已取消 的
        List<Map<String, Object>> messageDataList = message.getData().stream()
                .filter(each -> each.get("status") != null)
                .filter(each -> Objects.equals(each.get("status"), "30"))
                .toList();
        if (CollUtil.isEmpty(messageDataList)) {
            return;
        }
        // 2、解锁座位(mysql) + 令牌桶回滚(redis)
        for (Map<String, Object> each : messageDataList) {
            Result<TicketOrderDetailRespDTO> orderDetailResult = ticketOrderRemoteService.queryTicketOrderByOrderSn(each.get("order_sn").toString());
            TicketOrderDetailRespDTO orderDetailResultData = orderDetailResult.getData();
            if (orderDetailResult.isSuccess() && orderDetailResultData != null) {
                String trainId = String.valueOf(orderDetailResultData.getTrainId());
                List<TicketOrderPassengerDetailRespDTO> passengerDetails = orderDetailResultData.getPassengerDetails();
                seatService.unlock(trainId, orderDetailResultData.getDeparture(), orderDetailResultData.getArrival(), BeanUtil.convert(passengerDetails, TrainPurchaseTicketRespDTO.class));
                ticketAvailabilityTokenBucket.rollbackInBucket(orderDetailResultData);
            }
        }
    }

    @Override
    public String mark() {
        return CanalExecuteStrategyMarkEnum.T_ORDER.getActualTable();
    }

    @Override
    public String patternMatchMark() {
        return CanalExecuteStrategyMarkEnum.T_ORDER.getPatternMatchTable();
    }
}
